
package com.idealighter.game.games.xzdd.util;

import static com.idealighter.game.games.xzdd.struct.XzddMjType.TIAO;
import static com.idealighter.game.games.xzdd.struct.XzddMjType.TONG;
import static com.idealighter.game.games.xzdd.struct.XzddMjType.WAN;

import com.idealighter.game.games.xzdd.struct.XzddGangType;
import com.idealighter.game.games.xzdd.struct.XzddHuPaiType;
import com.idealighter.game.games.xzdd.struct.XzddHuType;
import com.idealighter.game.games.xzdd.struct.XzddMjType;
import com.idealighter.game.games.xzdd.struct.XzddSeat;
import com.idealighter.game.games.xzdd.struct.XzddSeatCards;
import com.idealighter.game.games.xzdd.struct.XzddTable;

import java.util.Arrays;

/**
 * 四川麻将胡牌分析工具类 .
 * 
 * @date 2016年8月23日 下午3:19:07
 *
 */
public class XzddCardsAnalyzer {

  private XzddCardsAnalyzer() {}

  /**
   * 胡的类型 .
   * 
   * @param cards .
   * @param mj .
   * @param isZiMo .
   * @return
   */
  public static XzddHuType huType(XzddSeatCards cards, int mj, boolean isZiMo) {
    XzddSeat seat = cards.seat;
    XzddTable table = seat.table;
    XzddSeat dealer = table.dealer;
    // 做了杠操作的玩家
    XzddSeat gangedSeat = table.gangedSeat();
    // 做了杠操作的玩家
    XzddSeat preGangedSeat = table.preGangedSeat;
    // 胡的类型
    XzddHuType huType = null;

    if (table.drawIndex == 53 && dealer.drawCount == 1 && seat == dealer
        && cards.getTotalCount() == 14 && table.nextPlaySeat == dealer) {
      huType = XzddHuType.TIAN_HU;
    } else if (seat.drawCount == 1 && (table.drawIndex > 53 || table.drawIndex < 57)
        && seat != dealer && cards.getTotalCount() == 14) {
      huType = XzddHuType.DI_HU;
    } else if (gangedSeat != null && gangedSeat != seat
        && gangedSeat.cards.gangType(mj) == XzddGangType.MIAN_XIA_GANG) {
      huType = XzddHuType.QIANG_GANG_HU;
    } else if (preGangedSeat != null && preGangedSeat == seat && isZiMo) {
      huType = XzddHuType.GANG_SHANG_HUA;
    } else if (preGangedSeat != null && preGangedSeat != seat && !isZiMo) {
      huType = XzddHuType.GANG_SHANG_PAO;
    } else {
      huType = XzddHuType.PING_HU;
    }

    return huType;
  }

  /**
   * 计算玩家的牌是否胡牌,必须缺一样且满足3n . + 2才能胡牌才能胡牌 连续三张牌叫做“顺”,三张同样的牌叫做“刻”,“二”表示的是“将”
   * 
   * @param cards .
   * @return null:不胡牌
   */
  public static XzddHuPaiType huPaiType(XzddSeatCards cards) {
    // 缺
    XzddMjType missing = cards.missing;

    int wanCount = cards.getWanCount();
    int tongCount = cards.getTongCount();
    int tiaoCount = cards.getTiaoCount();

    // 必须缺一样且满足3n + 2才能胡牌才能胡牌
    if (missing == WAN && wanCount > 0) {
      return null;
    } else if (missing == TONG && tongCount > 0) {
      return null;
    } else if (missing == TIAO && tiaoCount > 0) {
      return null;
    }

    // 龙对、小七对、大队统一判断
    XzddHuPaiType dui = isDui(cards);
    if (dui != null) {
      return dui;
    }

    // 是否平胡
    if (isPingHu(cards)) {
      return XzddHuPaiType.GENERAL;
    }

    return null;
  }

  /**
   * 是否对牌类型(龙对、小七对、大对) .
   * 
   * @param cards .
   * @return
   */
  private static XzddHuPaiType isDui(XzddSeatCards cards) {
    // 杠数量
    int gangCount = 0;
    // 对数量
    int duiCount = 0;

    int[][] mjs = cards.mjs;
    for (int typeIndex = 0; typeIndex < mjs.length; typeIndex++) {
      int[] tyMjs = mjs[typeIndex];
      for (int numIndex = 0; numIndex < tyMjs.length; numIndex++) {
        int count = tyMjs[numIndex];
        if (count == 4) {
          gangCount++;
          duiCount += 2;
        } else if (count == 3) {
          continue;
        } else if (count == 2) {
          duiCount++;
        } else if (count == 1) {
          return null;
        }
      }
    }

    if (gangCount == 1 && duiCount == 5) {
      return XzddHuPaiType.LONG_DUI;
    }

    if (duiCount == 7) {
      return XzddHuPaiType.XIAO_QI_DUI;
    }

    if (duiCount == 1 && gangCount == 0) {
      return XzddHuPaiType.DA_DUI;
    }

    return null;
  }

  /**
   * 是否平胡(普通胡牌),满足3n . + 2，先去掉对子，再看3n
   * 
   * @param cards .
   * @return
   */
  private static boolean isPingHu(XzddSeatCards cards) {
    int[][] mjs = cards.mjs;
    int typeLen = cards.mjs.length;

    for (int typeIndex = 0; typeIndex < typeLen; typeIndex++) {
      for (int numIndex = 0; numIndex < 9; numIndex++) {
        if (mjs[typeIndex][numIndex] > 1) {
          // 复制麻将,计算过程会改变值，先复制再计算
          int[][] mjsCopy = cards.copyMjs();
          int[] typeMjs = mjsCopy[typeIndex];
          // 去掉对子
          typeMjs[numIndex] -= 2;
          // 牌型是否满足3n，0张牌是满足3n的
          if (threeGroupAble(typeMjs) && threeGroupAble(mjsCopy[(typeIndex + 1) % typeLen])
              && threeGroupAble(mjsCopy[(typeIndex + 2) % typeLen])) {
            return true;
          }
        }
      }
    }

    return false;
  }

  /**
   * 同种麻将是否满足3n .
   * 
   * @param typeMjs .
   * @return
   */
  private static boolean threeGroupAble(int[] typeMjs) {
    for (int numIndex = 0; numIndex < 9; numIndex++) {
      if (!threeGroupAble(typeMjs, numIndex)) {
        return false;
      }
    }

    return true;
  }

  /**
   * 同种麻将是否满足3n(3顺和3刻)的组合,0是满足3n的 .
   * 
   * @param typeMjs .
   * @param numIndex . 麻将数字,从0开始算
   * @return
   */
  private static boolean threeGroupAble(int[] typeMjs, int numIndex) {
    // 数量
    int count = typeMjs[numIndex];
    /*
     * 如果是1张或2张，要胡牌的话他（们）必须和后面的牌组成顺子，如果不能组成顺子，肯定不胡 . 如果是3张,一是3张当作一个暗刻，二是这3张都与后面的牌组成顺子
     * 如果是4张,一是3张当作一个暗刻，1张当做顺,二是4张都做顺 .
     */
    if (count == 1) {
      return removeShun(typeMjs, numIndex);
    } else if (count == 2) {
      return removeShun(typeMjs, numIndex) && removeShun(typeMjs, numIndex);
    } else if (count == 3) {
      int[] mjsBackup = Arrays.copyOf(typeMjs, typeMjs.length);

      // 3张当做顺,剩下的牌必须组成3n
      boolean shunAble = true;
      if (shunAble = (removeShun(typeMjs, numIndex) && removeShun(typeMjs, numIndex)
          && removeShun(typeMjs, numIndex))) {
        for (int i = numIndex + 1; i < 9; i++) {
          if (!threeGroupAble(typeMjs, i)) {
            shunAble = false;
            break;
          }
        }
      }

      // 3张不能当做顺则当作暗刻
      if (!shunAble) {
        System.arraycopy(mjsBackup, 0, typeMjs, 0, typeMjs.length);
        return removeKe(typeMjs, numIndex);
      }
    } else if (count == 4) {
      int[] mjsBackup = Arrays.copyOf(typeMjs, typeMjs.length);

      // 4张都做顺
      if (!(removeShun(typeMjs, numIndex) && removeShun(typeMjs, numIndex)
          && removeShun(typeMjs, numIndex) && removeShun(typeMjs, numIndex))) {
        System.arraycopy(mjsBackup, 0, typeMjs, 0, typeMjs.length);
        // 3张当作一个暗刻，1张当做顺
        return removeKe(typeMjs, numIndex) && removeShun(typeMjs, numIndex);
      }
    }

    return true;
  }

  /**
   * 移除"3顺" .
   * 
   * @param typeMjs .
   * @param numIndex . 麻将数字,从0开始算
   * @return
   */
  private static boolean removeShun(int[] typeMjs, int numIndex) {
    if (numIndex > 6 || typeMjs[numIndex] == 0 || typeMjs[numIndex + 1] == 0
        || typeMjs[numIndex + 2] == 0) {
      return false;
    }

    typeMjs[numIndex]--;
    typeMjs[numIndex + 1]--;
    typeMjs[numIndex + 2]--;

    return true;
  }

  /**
   * 移除"刻" .
   * 
   * @param typeMjs .
   * @param numIndex . 麻将数字,从0开始算
   * @return
   */
  private static boolean removeKe(int[] typeMjs, int numIndex) {
    if (typeMjs[numIndex] < 3) {
      return false;
    }

    typeMjs[numIndex] -= 3;
    return true;
  }

}
